﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Distance Map Mode
    /// </summary>
    public enum DISTANCE_MAP_MODE
    {
        /// <summary>
        /// Own algorithm
        /// </summary>
        EXPERIMENTAL,

        /// <summary>
        /// 
        /// </summary>
        TRW_ALGORITHM
    }

    /// <summary>
    /// This filter is used to get a DISTANCE map from a stereo 3d image
    /// This needs two pics as an input.
    /// </summary>
    public class Distance : BaseImageFilter, ITwoSourceFilter
    {
        protected DISTANCE_MAP_MODE M_MODE = DISTANCE_MAP_MODE.EXPERIMENTAL;

        /// <summary>
        /// Initializes a new instance of the <see cref="Distance"/> class.
        /// </summary>
        public Distance()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Distance"/> class.
        /// </summary>
        /// <param name="_distance">The _distance.</param>
        internal Distance(Distance _distance)
        {
        }

        /// <summary>
        /// Gets or sets distance Map Mode
        /// </summary>
        public DISTANCE_MAP_MODE MODE
        {
            get { return M_MODE; }
            set { M_MODE = value; }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            //own approach
            return Process((UnsafeBitmap)_bitmap.Clone(), _bitmap);
        }

        #region ITwoSourceFilter Members

        /// <summary>
        /// Processes the specified bmpimg.
        /// </summary>
        /// <param name="_bitmap1"></param>
        /// <param name="_bitmap2"></param>
        /// <returns></returns>
        public UnsafeBitmap Process(UnsafeBitmap _bitmap1, UnsafeBitmap _bitmap2)
        {
            //find the most similar points in other pic...

            UnsafeBitmap bitmapResult = null;

            switch (M_MODE)
            {
                case DISTANCE_MAP_MODE.EXPERIMENTAL:
                    bitmapResult = ExperimentalDistanceMap(_bitmap1, _bitmap2);
                    break;

                case DISTANCE_MAP_MODE.TRW_ALGORITHM:
                    break;
            }

            return bitmapResult;
        }

        /// <summary>
        /// Experimental Mode
        /// This is an own algorithm that tries to find the stereo pairs.
        /// </summary>
        /// <param name="_bitmap1"></param>
        /// <param name="_bitmap2"></param>
        /// <returns></returns>
        private static UnsafeBitmap ExperimentalDistanceMap(UnsafeBitmap _bitmap1, UnsafeBitmap _bitmap2)
        {
            UnsafeBitmap bitmapResult = (UnsafeBitmap)_bitmap1.Clone();
            bitmapResult.Clear();
            int nWidth = _bitmap1.Width;
            int nHeight = _bitmap1.Height;
            int y = 0;
            int x = 0;

            //in which rectangle to search around the pixel
            int nWindow = 10;

            //Step 1 get similarities...
            for (y = nWindow; y < nHeight - nWindow; y++)
            {
                for (x = nWindow; x < nWidth - nWindow; x++)
                {
                    Color color1 = _bitmap2.GetPixel(x, y);
                    int nDiff = int.MaxValue;
                    int nX = 0;
                    int nY = 0;
                    int nTempDistance = 0;
                    int kx = 0;
                    int ky = 0;
                    for (ky = -nWindow / 2; ky < nWindow / 2; ky++)
                    {
                        for (kx = -nWindow / 2; kx < nWindow / 2; kx++)
                        {
                            Color color2 = _bitmap1.GetPixel(x + kx, y + ky);
                            nTempDistance = ColorFunctions.DistanceSquared(color1, color2);
                            if (nTempDistance < nDiff)
                            {
                                nDiff = nTempDistance;
                                nX = x + kx;
                                nY = y + ky;
                            }
                        }
                    }
                    double dDistance = (double)ImageMath.GetDistance(new Point(x, y), new Point(nX, nY));
                    if (dDistance == 0) dDistance = 1;
                    int nDistanceColor = (int)(255.0f / dDistance);
                    bitmapResult.SetGrayPixel(x, y, nDistanceColor);
                }
            }
            _bitmap1.Dispose();
            _bitmap2.Dispose();
            bitmapResult.Dispose();            
            return bitmapResult;
        }

        #endregion

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Distance(this);
        }
    }
}
